﻿using System;
using System.Threading;

/* disables warning on Interlocked.[In|De]crement(ref _count) */
#pragma warning disable 0420

namespace Needle.Utils.Threading
{

    #region Counter<T>

    /// <summary>
    /// A threadsafe counter, which executes the action 
    /// when the counter reaches 0. After the counter 
    /// has reached 0 it is unusable for further counting.
    /// </summary>
    /// <remarks>
    /// Adapted from Counting&lt;T&gt; from Joe Duffy's blog at
    /// http://www.bluebytesoftware.com/blog/2008-05-17-ConcurrentCounting.aspx
    /// </remarks>
    public class Counter<T>
    {

        private readonly T _item;
        private volatile int _count;
        private readonly Action<T> _action;

        /// <summary>
        /// Initializes a new instance of <see cref="Counter&lt;T&gt;"/>
        /// </summary>
        /// <param name="item">The item on which to perform the action, can be null.</param>
        /// <param name="initialCount">The initial count</param>
        /// <param name="action">The action to perform.</param>
        public Counter(T item, int initialCount, Action<T> action)
        {
            if (action == null)
                throw new ArgumentNullException("action", "action is null.");
            if (initialCount < 1)
                throw new ArgumentOutOfRangeException("initialCount", "The initialCount must be larger than 0");

            _item = item;
            _count = initialCount;
            _action = action;
        }
        /// <summary>
        /// Increments the counter
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// thrown when the counter has reached 0.
        /// </exception>
        public int Increment()
        {
            int c;
            if ((c = Interlocked.Increment(ref _count)) <= 1)
                throw new InvalidOperationException("Counter reached zero.");
            return c;
        }
        /// <summary>
        /// Decrements the count.
        /// </summary>
        public int Decrement()
        {
            int c;
            if ((c = Interlocked.Decrement(ref _count)) == 0)
                _action(_item);
            return c;
        }

        /// <summary>
        /// The item to count.
        /// </summary>
        public T Item
        {
            get { return _item; }
        }

        /// <summary>
        /// Increments the count.
        /// </summary>
        public static Counter<T> operator ++(Counter<T> counter)
        {
            counter.Increment();
            return counter;
        }
        /// <summary>
        /// Decrements the count.
        /// </summary>
        public static Counter<T> operator --(Counter<T> counter)
        {
            counter.Decrement();
            return counter;
        }
    }

    #endregion

    #region Counter

    /// <summary>
    /// A threadsafe counter, which executes the supplied action 
    /// when the counter reaches 0. After the counter 
    /// has reached 0 it is unusable for further counting.
    /// </summary>
    /// <remarks>
    /// Adapted from Counting&lt;T&gt; from Joe Duffy's blog at:
    /// <para>
    /// http://www.bluebytesoftware.com/blog/2008-05-17-ConcurrentCounting.aspx
    /// </para>
    /// </remarks>
    public class Counter
    {

        private volatile int _count;
        private readonly Action _action;

        /// <summary>
        /// Initializes a new instance of <see cref="Counter"/>
        /// </summary>
        /// <param name="initialCount">The initial count</param>
        /// <param name="action">The action to perform.</param>
        public Counter(int initialCount, Action action)
        {
            if (action == null)
                throw new ArgumentNullException("action", "action is null.");
            if (initialCount < 1)
                throw new ArgumentOutOfRangeException("initialCount", "The initialCount must be larger than 0");
            _count = initialCount;
            _action = action;
        }
        /// <summary>
        /// Increments the count
        /// </summary>
        public int Increment()
        {
            int c;
            if ((c = Interlocked.Increment(ref _count)) <= 1)
                throw new InvalidOperationException("Counter reached zero.");
            return c;
        }

        /// <summary>
        /// Decrements the count
        /// </summary>
        public int Decrement()
        {
            int c;
            if ((c = Interlocked.Decrement(ref _count)) == 0)
                _action();
            return c;
        }

        /// <summary>
        /// Increments the count
        /// </summary>
        public static Counter operator ++(Counter counter)
        {
            counter.Increment();
            return counter;
        }

        /// <summary>
        /// Decrements the count
        /// </summary>
        public static Counter operator --(Counter counter)
        {
            counter.Decrement();
            return counter;
        }
        
    }

    #endregion

}
